package cn.loioi.web.components.shiro;

import java.util.Date;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import cn.loioi.multi.enums.LogType;
import cn.loioi.multi.mysql.model.SysLog;
import cn.loioi.multi.service.SysLogService;
import cn.loioi.web.components.shiro.exception.TonKeyCodeException;

/**
 * 登录表单校验
 * @文件 :CustomFormAuthenticationFilter.java
 * @作者 :cn.loioi
 * @创建时间 :2016年10月13日 下午12:48:59
 */
public class CustomFormAuthenticationFilter extends FormAuthenticationFilter implements ApplicationContextAware {
	private Logger logger = LoggerFactory.getLogger(CustomFormAuthenticationFilter.class);
	private boolean useTonkey = false;// 是否使用验证码
	private String tonkeyParam = "tonkey";// 验证码的参数名

	private SysLogService logService;

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		logService = applicationContext.getBean(SysLogService.class);
	}

	/**
	 * 登录前置处理
	 */
	@Override
	public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
		logger.debug("onPreHandle");
		/**
		 * 如果已经登录，并且访问的地址是登录页面，直接重定向到登录成功页面
		 */
		if (isAccessAllowed(request, response, mappedValue)) {
			if (isLoginRequest(request, response)) {
				WebUtils.issueRedirect(request, response, getSuccessUrl());
				return false;
			} else {
				return true;
			}
		} else {
			return onAccessDenied(request, response, mappedValue);
		}

	}

	/**
	 * 执行登录操作
	 */
	@Override
	protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
		logger.info("executeLogin,RememberMe:" + isRememberMe(request));
		if (isUseTonkey()) {
			String tonkeyCode = getTonkey(request);
			String sessionTonkeyCode = getSessionTonkey(request, response);
			logger.debug("验证码：{}/{}", tonkeyCode, sessionTonkeyCode);
			if (tonkeyCode == null || !tonkeyCode.equalsIgnoreCase(sessionTonkeyCode)) { return onLoginFailure(null, new TonKeyCodeException(), request, response); }
		}
		return super.executeLogin(request, response);
	}

	/**
	 * 登录成功
	 */
	@Override
	protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
		logger.debug("onLoginSuccess");
		// 创建登录日志
		SysLog log = new SysLog();
		log.setUser(String.valueOf(token.getPrincipal()));
		log.setType(LogType.LOGIN.getCode());
		log.setHost(getHost(request));
		log.setTitle("用户登录");
		log.setContent("用户登录成功");
		log.setCreateTime(new Date());
		logService.insert(log);
		return super.onLoginSuccess(token, subject, request, response);
	}

	/**
	 * 登录成功
	 */
	@Override
	protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
		logger.error("onLoginFailure:" + e.getMessage());
		// 创建登录日志
		SysLog log = new SysLog();
		log.setUser(getUsername(request));
		log.setType(LogType.LOGIN.getCode());
		log.setHost(getHost(request));
		log.setTitle("用户登录");
		log.setContent("用户登录失败");
		log.setCreateTime(new Date());
		logService.insert(log);
		return super.onLoginFailure(token, e, request, response);
	}

	/**
	 * 登录失败
	 */
	@Override
	protected void setFailureAttribute(ServletRequest request, AuthenticationException e) {
		request.setAttribute("success", false);
		if (e instanceof TonKeyCodeException) {
			request.setAttribute("msg", "验证码错误");
		} else if (e instanceof UnknownAccountException || e instanceof IncorrectCredentialsException) {
			request.setAttribute("msg", "账号错误或者密码错误");
		} else {
			request.setAttribute("msg", "未知错误");
		}
	}

	/**
	 * 是否需要验证码
	 * @return
	 */
	public boolean isUseTonkey() {
		return useTonkey;
	}

	/**
	 * 设置是否需要验证码
	 * @param useTonkey
	 */
	public void setUseTonkey(boolean useTonkey) {
		this.useTonkey = useTonkey;
	}

	/**
	 * 获取验证码参数字段名
	 * @return
	 */
	public String getTonkeyParam() {
		return tonkeyParam;
	}

	/**
	 * 设置验证码参数字段名
	 * @param tonkeyParam
	 */
	public void setTonkeyParam(String tonkeyParam) {
		this.tonkeyParam = tonkeyParam;
	}

	/**
	 * 获取验证码
	 * @param request
	 * @return
	 */
	protected String getTonkey(ServletRequest request) {
		return request.getParameter(getTonkeyParam());
	}

	/**
	 * 获取缓存在Session里的验证码
	 * @param request
	 * @param response
	 * @return
	 */
	protected String getSessionTonkey(ServletRequest request, ServletResponse response) {
		Subject subject = getSubject(request, response);
		if (subject == null) return null;
		Session session = subject.getSession(false);
		if (session == null) return null;
		return String.valueOf(session.getAttribute(getTonkeyParam()));
	}
}
